<script lang="ts" setup>
import { computed, reactive, ref, watch, useSlots, onMounted, onUnmounted } from 'vue'
import {
  TableImport,
  TableExport,
  TableSetting,
  TableFilter,
  TableIndex,
  TableEmpty,
  TableTag,
} from '@/components/sofastui/AdvancedTable'
import { usePagination } from 'vue-request'
import { http } from '@/services/request'
import type { IPaginatedResponse } from '@/services/request/types'
import type {
  IConfigProps,
  IFieldProps,
  IPaginationResult,
  ISlots,
  ITableFilter,
  ITableQuery,
  ITableSelection,
  ITableSort,
} from './interface'

// Types

// Props
const props = withDefaults(
  defineProps<{
    headerHeight?: string
    hideHeader?: boolean
    className?: string
    fields?: IFieldProps[]
    config?: IConfigProps
    query?: ITableQuery
  }>(),
  {
    headerHeight: '46px',
    hideHeader: false,
    className: '',
    fields: () => [],
    config: () => ({
      url: '',
    }),
    query: () => ({}),
  },
)

// Emits
const emit = defineEmits<{
  (e: 'rowExpand', data: Record<string, unknown>): void
  (e: 'add'): void
}>()

// Refs
const tableImportRef = ref<InstanceType<typeof TableImport>>()
const tableContainerRef = ref<HTMLElement>()
const tableHeightNum = ref<number>()
const tableHeight = ref<string>('100%')
const windowHeight = ref<number>()
const multipleSelection = ref<ITableSelection[]>([])
const slots = useSlots() as ISlots

// Constants
const pageSizes = [10, 20, 50, 100, 200]

// Services
const serve = {
  add: (data: Record<string, unknown>) =>
    http.request({
      url: props.config.url,
      method: 'POST',
      data,
    }),
  del: (id: string) =>
    http.request({
      url: `${props.config.url}/${id}`,
      method: 'DELETE',
    }),
  dels: (ids: string[]) =>
    http.request({
      url: props.config.url,
      method: 'DELETE',
      data: { ids },
    }),
  update: (data: Record<string, unknown>) =>
    http.request({
      url: `${props.config.url}/${data.id}`,
      method: 'PUT',
      data,
    }),
  getObj: (id: string) =>
    http.request({
      url: `${props.config.url}/${id}`,
      method: 'GET',
    }),
  getList: (params: Record<string, unknown>) =>
    http.request({
      url: props.config.url,
      method: 'GET',
      params,
    }),
  getPage: (params: Record<string, unknown>) =>
    http.request<IPaginatedResponse<IPaginationResult>>({
      url: props.config.url,
      method: 'GET',
      params,
    }),
}

// Reactive state
const searchFormData = reactive<{
  searchKey: string
  pageSize: number
  filter: ITableFilter
  order: [string, 'descending' | 'ascending'][]
  [key: string]: unknown
}>({
  searchKey: '',
  pageSize: 20,
  filter: { relation: 'or', fields: [] },
  order: [],
  ...props.query,
})

// Computed
const tabelFields = computed(() =>
  props.fields.filter((item) => item.select !== false).map((item) => ({ ...item })),
)

const searchFields = computed(() =>
  props.fields.filter((item) => !!item.search).map((item) => ({ ...item })),
)

const importFields = computed(() =>
  props.fields.filter((item) => !!item.import).map((item) => ({ ...item })),
)

const exportFields = computed(() =>
  props.fields.filter((item) => !!item.export).map((item) => ({ ...item })),
)

const showOperate = computed(() => {
  return (
    hasSlotContent('table-row-operate') &&
    (props.config.operate === true ||
      (props.config.operate !== false && props.config.operate?.show === true))
  )
})
const showLeftOperate = computed(
  () => props.config.add || props.config.import || hasSlotContent('table-top-left'),
)
const showToolBars = computed(
  () =>
    props.config.advancedSearch ||
    props.config.refresh ||
    props.config.settings ||
    props.config.export,
)

// Pagination
const { run, refresh, data, current, loading, pageSize, total } = usePagination(serve.getPage, {
  defaultParams: [{ ...searchFormData }],
  pagination: {
    currentKey: 'page',
    pageSizeKey: 'pageSize',
    totalKey: 'total',
  },
})

// Methods
const setTableContainerHeight = () => {
  windowHeight.value = window.innerHeight
  const dom = tableContainerRef.value
  if (dom) {
    const style = getComputedStyle(dom)
    const height = dom.clientHeight - parseInt(style.paddingTop) - parseInt(style.paddingBottom)
    tableHeightNum.value = height
    tableHeight.value = `${height}px`
  }
}

const handleResize = () => {
  if (windowHeight.value !== window.innerHeight) {
    tableHeight.value = `${tableHeightNum.value! + (window.innerHeight - windowHeight.value!)}px`
  }
}

const hasSlotContent = (slotNames: string | string[]): boolean => {
  if (typeof slotNames === 'string') {
    return !!slots[slotNames]?.()
  }
  return slotNames.some((slotName) => !!slots[slotName]?.())
}

const handelAdd = () => emit('add')
const handelRowExpand = (data: Record<string, unknown>) => emit('rowExpand', data)
const handelImport = () => tableImportRef.value?.show()

const handleBatch = async (type: string) => {
  if (type === 'dels') {
    try {
      await ElMessageBox.confirm(
        `批量删除当前选中${multipleSelection.value.length}条数据, 是否继续?`,
        '提示',
        {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning',
        },
      )
      await serve.dels(multipleSelection.value.map((item) => item.id))
      ElMessage.success('删除成功')
      refresh()
    } catch {
      ElMessage.info('已取消删除')
    }
  }
}

const handleSelectionChange = (val: ITableSelection[]) => {
  multipleSelection.value = val
}

const handleSortChange = ({ prop, order }: ITableSort) => {
  searchFormData.order = order ? [[prop, order]] : []
  run({
    ...searchFormData,
    currentPage: 1,
    pageSize: pageSize.value,
  })
}

// Lifecycle
onMounted(() => {
  setTableContainerHeight()
  window.addEventListener('resize', handleResize)
})

onUnmounted(() => {
  window.removeEventListener('resize', handleResize)
})

// Watchers
watch(
  () => props.query,
  (newQuery) => {
    Object.assign(searchFormData, newQuery)
    run({ ...searchFormData })
  },
  { deep: true },
)

// Expose
defineExpose({ refresh, handelAdd, handelImport })
</script>

<template>
  <div class="flex flex-col w-full h-full bg-white">
    <div
      class="flex px-2 pt-2"
      :class="{
        'justify-between': showLeftOperate,
        'justify-end': !showLeftOperate,
      }"
    >
      <el-space>
        <!-- 新增 -->
        <el-button v-if="config.add" type="primary" icon="Notification" @click="handelAdd">
          {{ typeof config.add === 'object' ? config.add.title : '新增' }}
        </el-button>

        <!-- 数据导入弹窗 -->
        <TableImport
          ref="tableImportRef"
          v-if="config.import"
          :fields="importFields"
          downTmp="/download/user_temp.xlsx"
          @success="refresh()"
        />
        <slot name="table-top-left" />
      </el-space>

      <el-space>
        <el-input
          v-if="config.search"
          v-model="searchFormData.searchKey"
          class="w-50 mr-2"
          placeholder="请输入关键词搜索"
          suffix-icon="Search"
          clearable
          @change="
            run({
              ...searchFormData,
              currentPage: 1,
              pageSize: pageSize,
            })
          "
        />

        <el-button-group v-if="showToolBars" class="ml-4">
          <!-- 表格高级搜素 -->
          <TableFilter
            v-if="config.advancedSearch"
            v-model="searchFormData.filter"
            :fields="searchFields"
            @change="
              run({
                ...searchFormData,
                currentPage: 1,
                pageSize: pageSize,
              })
            "
          />

          <el-tooltip v-if="config.refresh" effect="dark" content="刷新" placement="top">
            <el-button type="primary" icon="Refresh" plain @click="refresh()" :loading="loading" />
          </el-tooltip>

          <!-- 表格设置 -->
          <TableSetting v-if="config.settings" :fields="exportFields" />

          <!-- 数据导出弹窗 -->
          <TableExport v-if="config.export" :fields="exportFields" />
        </el-button-group>
      </el-space>
    </div>

    <div class="table-container flex-1 p-2" ref="tableContainerRef">
      <el-table
        :data="data?.list || []"
        :height="tableHeight"
        max-height="100%"
        v-loading="loading"
        table-layout="auto"
        scrollbar-always-on
        border
        :row-class-name="config.expand ? 's-table-row' : ''"
        :row-key="config.rowKey || 'id'"
        show-overflow-tooltip
        @selection-change="handleSelectionChange"
        @sort-change="handleSortChange"
      >
        <template #empty>
          <TableEmpty :loading="loading" @refresh="refresh()" />
        </template>

        <el-table-column v-if="config.selection" type="selection" width="44" align="center" />

        <el-table-column
          v-if="config.showIndex"
          type="index"
          fixed
          label="序号"
          width="60"
          align="center"
        >
          <template #default="scope">
            <TableIndex
              :index="scope.$index + 1"
              :expand="config.expand"
              @open="handelRowExpand(scope.row)"
            />
          </template>
        </el-table-column>

        <el-table-column
          v-for="(item, index) in tabelFields"
          :key="index"
          :prop="item.prop"
          :label="item.label"
          :align="item.align"
          :width="item.width"
          :min-width="item.minWidth"
          :sortable="item.sort"
          :fixed="item.fixed"
        >
          <template v-if="item.isTag" #default="scope">
            <TableTag :scope="scope" :item="item as any" />
          </template>
          <template v-else-if="item.slot" #default="scope">
            <slot :name="item.prop" v-bind="scope" />
          </template>
        </el-table-column>

        <el-table-column
          v-if="showOperate"
          :label="typeof config.operate === 'object' ? config.operate.label : '操作'"
          :width="typeof config.operate === 'object' ? config.operate.width : 80"
          align="center"
        >
          <template #default="scope">
            <slot name="table-row-operate" v-bind="scope" />
          </template>
        </el-table-column>
      </el-table>
    </div>

    <div class="table-footer flex items-center justify-between px-2 pb-2 overflow-hidden">
      <div class="demonstration">
        <el-button
          v-if="config.delete && config.selection"
          text
          bg
          type="danger"
          :disabled="!multipleSelection.length"
          @click="handleBatch('dels')"
        >
          删除
        </el-button>
      </div>

      <el-pagination
        background
        v-model:current-page="current"
        v-model:page-size="pageSize"
        :total="total"
        :page-sizes="pageSizes"
        :default-page-size="20"
        layout="total, sizes, prev, pager, next, jumper"
      />
    </div>
  </div>
</template>
